home *** CD-ROM | disk | FTP | other *** search
/ SGI Hot Mix 17 / Hot Mix 17.iso / HM17_SGI / research / lib / ascii_template.pro < prev    next >
Text File  |  1997-07-08  |  47KB  |  1,421 lines

  1. ; $Id: ascii_template.pro,v 1.12 1997/04/22 21:54:29 mark Exp $
  2. ;
  3. ; Copyright (c) 1996-1997, Research Systems, Inc.  All rights reserved.
  4. ;       Unauthorized reproduction prohibited.
  5. ;+
  6. ; NAME:
  7. ;    ASCII_TEMPLATE
  8. ;
  9. ; PURPOSE:
  10. ;    Generate a template that defines an ASCII file format.
  11. ;
  12. ; CATEGORY:
  13. ;    Input/Output.
  14. ;
  15. ; CALLING SEQUENCE:
  16. ;    template = ASCII_TEMPLATE( [file] )
  17. ;
  18. ; INPUTS:
  19. ;    file          - Name of file to base the template on.
  20. ;                Default = use DIALOG_PICKFILE to get the file.
  21. ;
  22. ; INPUT KEYWORD PARAMETERS:
  23. ;    browse_lines      - Number of lines to read in at a time via the
  24. ;                GUI's browse button.  Default = 50.
  25. ;
  26. ; OUTPUT KEYWORD PARAMETERS:
  27. ;    cancel            - Boolean indicating if the user canceled
  28. ;                out of the interface (1B = canceled).
  29. ;
  30. ; OUTPUTS:
  31. ;    The function returns a template (structure) defining ASCII files
  32. ;    of the input file's format.  Such templates may be used as inputs
  33. ;    to function READ_ASCII.  (0 is returned if the user canceled.)
  34. ;
  35. ; COMMON BLOCKS:
  36. ;    None.
  37. ;
  38. ; SIDE EFFECTS:
  39. ;    None.
  40. ;
  41. ; RESTRICTIONS:
  42. ;    See DESCRIPTION.
  43. ;
  44. ; DESCRIPTION:
  45. ;    This routine presents a graphical user interface (GUI) that assists
  46. ;    the user in defining a template.
  47. ;
  48. ;    ASCII files handled by this routine consist of an optional header
  49. ;    of a fixed number of lines, followed by columnar data.  Files may
  50. ;    also contain comments, which exist between a user-specified comment
  51. ;    string and the corresponding end-of-line.
  52. ;
  53. ;    One or more rows of data constitute a "record."  Each data element
  54. ;    within a record is considered to be in a different column, or "field."
  55. ;    Adjacent fields may be "grouped" into multi-column fields.
  56. ;    The data in one field must be of, or promotable to, a single
  57. ;    type (e.g., FLOAT).
  58. ;
  59. ; EXAMPLES:
  60. ;    ; Generating a template to be used later, maybe on a set of files.
  61. ;    template = ASCII_TEMPLATE()
  62. ;
  63. ;    ; Same as above, but immediately specifying which file to use.
  64. ;    template = ASCII_TEMPLATE(file)
  65. ;
  66. ;    ; Same as above, but returning flag if the user canceled.
  67. ;    template = ASCII_TEMPLATE(file, CANCEL=cancel)
  68. ;
  69. ;    ; Generating and using a template in place for reading data.
  70. ;    data = READ_ASCII(file, TEMPLATE=ASCII_TEMPLATE(file))
  71. ;
  72. ; DEVELOPMENT NOTES:
  73. ;    - see ???,!!!,xxx in the code
  74. ;    - errors preserving state when switch pages with 'back/next'
  75. ;    - make NaN default missing value as in reader ?
  76. ;
  77. ; MODIFICATION HISTORY:
  78. ;    AL & RPM, 8/96 - Written.
  79. ;
  80. ;-
  81. ;
  82. ; ROUTINES:
  83. ;    fun at_build_templ    - build a template
  84. ;    pro at_delete_template    - delete a template
  85. ;    fun at_get_lines    - read lines from file
  86. ;    fun at_build_template    - build a template
  87. ;    fun at_num_fields    -
  88. ;    fun at_default_delimit    -
  89. ;    fun at_default_groups    -
  90. ;    fun at_which_field    -
  91. ;    fun at_str_to_val    -
  92. ;    fun at_list_to_str    -
  93. ;    fun at_str_to_list    -
  94. ;       pro at_remove_tabs      -
  95. ;    pro at_display_text    -
  96. ;    pro at_resize_table    -
  97. ;    pro at_sample_record    -
  98. ;    pro at_set_list        -
  99. ;    pro at_update        -
  100. ;    pro at_3_event        -
  101. ;    pro at_2_event        -
  102. ;    pro at_1_event        -
  103. ;    pro at_set_state    -
  104. ;    pro at_widget_cleanup    -
  105. ;    pro at_widget_event    -
  106. ;    fun at_widget        - dialog to generate a template
  107. ;       fun at_check_file       - check validity of input file
  108. ;    fun ascii_template    - the main routine
  109.  
  110. ; -----------------------------------------------------------------------------
  111. ;
  112. ;  Purpose:  Build an ASCII file template, using defaults as necessary.
  113. ;
  114. function at_build_templ, $
  115.     num_fields=num_fields, $
  116.     field_types=field_types, $
  117.     field_names=field_names, $
  118.     field_locations=field_locations, $
  119.     record_start_loc=record_start_loc, $
  120.     delimiter=delimiter, $
  121.     groups=groups, $
  122.     missing_value=missing_value, $
  123.     comment_symbol=comment_symbol
  124.  
  125.   if (n_elements(num_fields) eq 0) then num_fields = 1
  126.   tot_num_fields = total(num_fields)
  127.  
  128.   if (n_elements(field_types) eq 0) then field_types = 4L
  129.   if (n_elements(field_locations) eq 0) then $
  130.     field_locations = lonarr(tot_num_fields)
  131.   if (n_elements(field_names) eq 0) then begin
  132.     digits_str = string(strlen(strtrim(string(fix(tot_num_fields)),2)))
  133.     fstr = '(i' + strtrim(digits_str,2) + '.' + strtrim(digits_str,2) + ')'
  134.     field_names = 'field' + string(indgen(tot_num_fields)+1,format=fstr)
  135.   endif
  136.   ;
  137.   if (n_elements(record_start_loc) eq 0) then record_start_loc = 0L
  138.   if (n_elements(delimiter) eq 0) then delimiter = 0B
  139.   if (n_elements(missing_value) eq 0) then missing_value = 0.0
  140.   if (n_elements(groups) eq 0) then groups = indgen(tot_num_fields)
  141.   if (n_elements(comment_symbol) eq 0) then comment_symbol = ''
  142.   ;
  143.   ; define the ASCII template structure
  144.   ;
  145.   fields = replicate({ascii_field_struct, name:'', type:0, loc:0L}, $
  146.     tot_num_fields)
  147.   if (tot_num_fields eq 1) then begin
  148.     fields.name = field_names(0)
  149.     fields.type = field_types(0)
  150.     fields.loc  = field_locations(0)
  151.   endif else begin
  152.     fields.name = field_names
  153.     fields.type = field_types
  154.     fields.loc  = field_locations
  155.   endelse
  156.  
  157.   template = { $
  158.     record_start_loc: record_start_loc, $
  159.     delimit: delimiter(0), $
  160.     missing_value: float(missing_value), $    ; Why FLOAT() ???
  161.     comment_symbol: comment_symbol, $
  162.     p_num_fields: ptr_new(num_fields), $
  163.     p_fields: ptr_new(fields), $
  164.     p_groups: ptr_new(groups) $
  165.     }
  166.  
  167.   return, template
  168.  
  169. end            ; at_build_templ
  170.  
  171. ; -----------------------------------------------------------------------------
  172. ;
  173. ;  Purpose:  Delete an ASCII file template.
  174. ;
  175. pro at_delete_template, template
  176.   info = size(template)
  177.   if (info(info(0)+1) ne 8) then return
  178.   ;
  179.   if (ptr_valid(template.p_num_fields)) then ptr_free, template.p_num_fields
  180.   if (ptr_valid(template.p_fields)) then ptr_free, template.p_fields
  181.   if (ptr_valid(template.p_groups)) then ptr_free, template.p_groups
  182.  
  183. end            ; at_delete_template
  184.  
  185. ; -----------------------------------------------------------------------------
  186. ;
  187. ;  Purpose:  Return a requested number of lines from the ASCII file.
  188. ;         If skip is set, then skip the first skip number of lines before
  189. ;         starting the read.
  190. ;         Return the last_pos to resume reading from a given point and
  191. ;         notify when the end of file has been reached.
  192. ;
  193. function at_get_lines, name, num_lines, last_pos=last_pos, $
  194.   end_reached=end_reached, skip=skip
  195.   ;
  196.   catch, error_status
  197.   if (error_status ne 0) then begin
  198.     end_reached = 1
  199.     if (n_elements(unit) gt 0) then free_lun, unit
  200.     return, ''
  201.   endif
  202.   ;
  203.   if (n_elements(last_pos) eq 0) then last_pos = 0
  204.   if (n_elements(skip) eq 0) then skip = 0
  205.   lines = strarr(num_lines)
  206.   line = ''
  207.   count = 0L
  208.   openr, unit, name, /get_lun
  209.   point_lun, unit, last_pos
  210.   for i=0, skip-1 do readf, unit, line
  211.   while (not eof(unit) and count lt num_lines) do begin
  212.     readf, unit, line
  213.     lines(count) = line
  214.     count = count + 1
  215.   endwhile
  216.   ;
  217.   end_reached = (count lt num_lines)
  218.   point_lun, -unit, last_pos
  219.   ;
  220.   free_lun, unit
  221.   if (count eq 0) then              return, '' $
  222.   else if (count lt num_lines) then return, lines(0:count-1) $
  223.   else                              return, lines
  224.  
  225. end            ; at_get_lines
  226.  
  227. ; -----------------------------------------------------------------------------
  228. ;
  229. ;  Purpose:  Build a default template structure.
  230. ;
  231. function at_build_template, data, missing_value
  232.  
  233.   ; Use the first record from the file (ignoring comment strings
  234.   ; and blank lines).
  235.   ;
  236.   lines = (*data.p_rlines)(0:n_elements(*data.p_num_fields)-1)
  237.  
  238.   ; lut is used to determine if a given field is integer,
  239.   ; floating point, or a string.
  240.   ;
  241.   lut = bytarr(256) + 1b
  242.   lut(0:32) = 0b
  243.   lut(48:57) = 0b
  244.   lut(byte('e')) = 0
  245.   lut(byte('E')) = 0
  246.   lut(byte('+')) = 0
  247.   lut(byte('-')) = 0
  248.   lut(byte('.')) = 0
  249.   ;
  250.   ; scan through a sample record and determine a default column
  251.   ; position and IDL type for each field.
  252.   ;
  253.   tot_num_fields  = long(total(*data.p_num_fields))
  254.   field_locations = lonarr(tot_num_fields)
  255.   field_types     = intarr(tot_num_fields)
  256.   fpos = 0L
  257.   for i=0, n_elements(*data.p_num_fields)-1 do begin
  258.     bline = [byte(lines(i)), 32b]
  259.  
  260.     if (data.delimit eq 32b) then begin       ; delimiter is 'space'
  261.       nptr = where(bline ne 32b and bline ne 9b, ncount)
  262.       fptr = nptr(0)
  263.       for j=1, ncount-1 do $
  264.         if (nptr(j) gt nptr(j-1)+1) then fptr = [fptr, nptr(j)]
  265.       fptr = [fptr, n_elements(bline)]
  266.       add = [0,1]
  267.  
  268.     endif else begin
  269.       nptr = where(bline eq data.delimit, ncount)
  270.       if (ncount eq 0) then fptr = [-1, n_elements(bline)] $
  271.       else                  fptr = [-1, nptr, n_elements(bline)]
  272.       add = [1,1]
  273.     endelse
  274.  
  275.     ;
  276.     for j=0, (n_elements(fptr)-2)<((*data.p_num_fields)(i)-1) do begin
  277.       field_locations(fpos) = fptr(j) + add(0)
  278.       bsub = bline(fptr(j)+add(0):(fptr(j+1)-add(1))>(fptr(j)+add(0)))
  279.       is_string = (total(lut(bsub)) gt 0)
  280.       if (is_string eq 0) then begin
  281.         if (total(bsub eq 46b) gt 0 or total(bsub eq 101b) gt 0 or $
  282.                  total(bsub eq 69b) gt 0) then field_types(fpos) = 4 $
  283.         else $
  284.           field_types(fpos) = 3
  285.       endif else $
  286.         field_types(fpos) = 7
  287.       fpos = fpos + 1
  288.     endfor
  289.   endfor
  290.   ;
  291.   s_delimit = ([0b, data.delimit])(data.mode)
  292.  
  293.   template = at_build_templ(num_fields=*data.p_num_fields, $
  294.     field_types=field_types, field_locations=field_locations, $
  295.     record_start_loc=data.data_start, delimiter=s_delimit, $
  296.     missing_value=missing_value, comment_symbol=data.comment)
  297.  
  298.   return, template
  299.  
  300. end            ; at_build_template
  301.  
  302. ; -----------------------------------------------------------------------------
  303. ;
  304. ;  Purpose:  Return an array of number of lines long the number of fields
  305. ;         contained in each record based upon where the number of
  306. ;         delimiters found.
  307. ;
  308. function at_num_fields, lines, delimit, comment
  309.   ;
  310.   for i=0, n_elements(lines)-1 do begin
  311.     bline = byte(lines(i))
  312.  
  313.     if (delimit eq 32b) then begin       ; delimiter is 'space'
  314.       nptr = where(bline ne 32b and bline ne 9b, count)
  315.       fptr = nptr(0)
  316.       for j=1, count-1 do $
  317.         if (nptr(j) gt nptr(j-1)+1) then fptr = [fptr, nptr(j)]
  318.  
  319.     endif else begin
  320.       nptr = where(bline eq delimit, count)
  321.       fptr = bytarr(count+1)
  322.     endelse
  323.  
  324.     ;
  325.     if (n_elements(num_fields) eq 0) then num_fields = n_elements(fptr) $
  326.     else begin
  327.       if (n_elements(fptr) eq num_fields(0)) then return, num_fields
  328.       num_fields = [num_fields, n_elements(fptr)]
  329.     endelse
  330.   endfor
  331.   if (n_elements(num_fields) gt 0) then return, num_fields $
  332.   else                                  return, 1
  333. end            ; at_num_fields
  334.  
  335. ; -----------------------------------------------------------------------------
  336. ;
  337. ;  Purpose:  Given the first line of data make a guess as to the delimiter
  338. ;         in use.
  339. ;
  340. function at_default_delimit, line, comment
  341.  
  342.   if (comment ne '') then begin
  343.     pos = strpos(line, comment, 0)
  344.     if (pos(0) ge 0) then line = strmid(line, 0, pos(0)-1)
  345.   endif
  346.  
  347.   pos = strpos(line, ',')
  348.   if (pos(0) ge 0) then return, 44B
  349.  
  350.   pos = strpos(line, ';')
  351.   if (pos(0) ge 0) then return, 59B
  352.  
  353. ; dont assume that the delimiter can be a colon...
  354. ;  pos = strpos(line, ':')
  355. ;  if (pos(0) ge 0) then return, 58B
  356.  
  357.   ; Default return 'space' as the delimiter.
  358.   ;
  359.   return, 32B
  360.  
  361. end            ; at_default_delimit
  362.  
  363. ; -----------------------------------------------------------------------------
  364. ;
  365. ;  Purpose:  Return a best guess of the groupings of data base upon initial
  366. ;         data type.
  367. ;
  368. function at_default_groups, data
  369.  
  370.   types = (*data.p_fields).type
  371.   groups = intarr(n_elements(types))
  372.   cur_group = 0
  373.  
  374.   for i=1, n_elements(types)-1 do begin
  375.     if (types(i) eq types(i-1)) then groups(i) = groups(i-1) $
  376.     else begin
  377.       cur_group = cur_group + 1
  378.       groups(i) = cur_group
  379.     endelse
  380.   endfor
  381.  
  382.   return, groups
  383.  
  384. end            ; at_default_groups
  385.  
  386. ; -----------------------------------------------------------------------------
  387. ;
  388. ;  Purpose:  Given a position between 0 and tot_num_fields-1 determine
  389. ;         the line and location on the line of the given field.
  390. ;
  391. function at_which_field, num_fields, pos, col
  392.   ;
  393.   count = 0
  394.   for i=0, n_elements(num_fields)-1 do begin
  395.     if (pos lt count+num_fields(i)) then begin
  396.       col = pos - count
  397.       return, i
  398.     endif
  399.     count = count + num_fields(i)
  400.   endfor
  401.  
  402. end            ; at_which_field
  403.  
  404. ; -----------------------------------------------------------------------------
  405. ;
  406. ;  Purpose:  Convert a scaler string into a long or floating point value,
  407. ;            return 0 for any problems.
  408. ;
  409. function at_str_to_val, str, floating=floating
  410.   ;
  411.   catch, error_status
  412.   if (error_status ne 0) then return, 0
  413.   if (keyword_set(floating)) then temp = 0. $
  414.   else                            temp = 0L
  415.   reads, str(0), temp
  416.   return, temp
  417.  
  418. end            ; at_str_to_val
  419.  
  420. ; -----------------------------------------------------------------------------
  421. ;
  422. ;  Purpose:  Convert an array of numbers into a string of comma sepearted
  423. ;         values.
  424. ;
  425. function at_list_to_str, vals
  426.   ;
  427.   str = strtrim(string(vals(0)),2)
  428.   for i=1, n_elements(vals)-1 do $
  429.     str = str + ',' + strtrim(string(vals(i)),2)
  430.   return, str
  431.  
  432. end            ; at_list_to_str
  433.  
  434. ; -----------------------------------------------------------------------------
  435. ;
  436. ;  Purpose:  Given a string of comma separated values, convert into an array
  437. ;         of values.
  438. ;
  439. function at_str_to_list, str
  440.   ;
  441.   len = strlen(str(0))
  442.   ptr = where(byte(str(0)) eq 44b, count)
  443.   sub = (strmid(str(0),len-1,1) eq ',')
  444.   count = count - sub
  445.   vals = lonarr(count+1)
  446.   vals(0) = at_str_to_val(str(0))
  447.   for i=0, count-1 do $
  448.     vals(i+1) = at_str_to_val(strmid(str(0),ptr(i)+1,len))
  449.   return, vals
  450.  
  451. end            ; at_str_to_list
  452.  
  453. ; -----------------------------------------------------------------------------
  454. ;
  455. ;  Purpose:  Scan through the string array and replace any instance of
  456. ;            tab (9b) with four white spaces.
  457. ;
  458. pro at_remove_tabs, lines
  459.   s_tab = string(9b)
  460.   for i=0, n_elements(lines)-1 do begin
  461.     loc = 0l
  462.     len = strlen(lines(i))
  463.     repeat begin
  464.       pos = strpos(lines(i), s_tab, loc)
  465.       if (pos ge 0) then begin
  466.         lines(i) = strmid(lines(i), 0, pos) + '    ' + $
  467.                    strmid(lines(i), pos+1, len)
  468.         loc = pos + 1
  469.       endif
  470.     endrep until (pos eq -1)
  471.   endfor
  472. end            ; at_remove_tabs
  473.  
  474.  
  475. ; -----------------------------------------------------------------------------
  476. ;
  477. ;  Purpose:  Display the appropriate text into the main table widget.
  478. ;
  479. pro at_display_text, data, first=first
  480.   if (keyword_set(first)) then begin
  481.     top_pos = widget_info(data.tw(6), /table_view)
  482.     num_lines = n_elements(*data.p_lines)
  483.     rstr = strtrim(string(indgen(num_lines)+1),2)
  484.     if (total(byte(*data.p_lines) eq 9b) gt 0) then begin
  485.       lines = *data.p_lines
  486.       at_remove_tabs, lines
  487.       lines = reform(lines, 1, num_lines, /overwrite)
  488.     endif else $
  489.       lines = reform(*data.p_lines, 1, num_lines)
  490.     widget_control, data.tw(6), set_table_view=top_pos, set_value=lines, $
  491.       row_labels=rstr, set_table_select=[0,data.data_start<(data.twm(1)-1), $
  492.                                          0,data.data_start<(data.twm(1)-1)]
  493.   endif else begin
  494.     num_lines = n_elements(*data.p_rlines)
  495.     rstr = strtrim(string(indgen(num_lines)+1),2)
  496.     if (total(byte(*data.p_rlines) eq 9b) gt 0) then begin
  497.       lines = *data.p_rlines
  498.       at_remove_tabs, lines
  499.       lines = reform(lines, 1, num_lines, /overwrite)
  500.     endif else $
  501.       lines = reform(*data.p_rlines, 1, num_lines)
  502.     widget_control, data.tw(6), set_table_view=[0,0], set_value=lines, $
  503.       row_labels=rstr, set_table_select=[-1,-1,-1,-1]
  504.   endelse
  505. end            ; at_display_text
  506.  
  507. ; -----------------------------------------------------------------------------
  508. ;
  509. ;  Purpose:  Resize a given table widget to a new number of row / col cells.
  510. ;
  511. pro at_resize_table, tw, prev_size, new_size, text_table=text_table, last=last
  512. ;
  513. ; there are two different table widgets used in this widget. if the text_table
  514. ; keyword is set, then this refers to the table widget on the bottom which
  515. ; displays the text in STEP 1, the data in STEP 2 and a sample record in
  516. ; STEP 3, if not set, then this refers to the table wdiget in STEP 3 in the
  517. ; upper left corner which displays the field information.
  518. ;
  519.   if (prev_size(0) eq new_size(0) and prev_size(1) eq new_size(1)) then return
  520.   ;
  521.   col_diff = prev_size(0) - new_size(0)
  522.   row_diff = prev_size(1) - new_size(1)
  523.   ;
  524.   if (prev_size(1)-new_size(1) gt 0) then $
  525.     widget_control, tw, delete_rows=prev_size(1)-new_size(1), $
  526.       use_table_select=[0,new_size(1),prev_size(0)-1,prev_size(1)-1] $
  527.   else if (prev_size(1)-new_size(1) lt 0) then $
  528.     widget_control, tw, insert_rows=-(prev_size(1)-new_size(1))
  529.   ;
  530.   if (col_diff gt 0) then $
  531.     widget_control, tw, delete_columns=col_diff, $
  532.       use_table_select=[new_size(0),0,prev_size(0)-1,new_size(1)-1] $
  533.   else if (col_diff lt 0) then $
  534.     widget_control, tw, insert_columns=-col_diff
  535.   ;
  536.   if (keyword_set(text_table)) then begin
  537.   ; the text table has a different look in STEP 3 then in STEP 1 & 2...
  538.   ;
  539.     if (keyword_set(last)) then $
  540. ;      widget_control, tw, column_widths=.75, units=1 $
  541.       widget_control, tw, column_widths=90 $
  542.     else begin
  543.       widget_control, tw, column_widths=450, column_labels=['Text']
  544.       widget_control, tw, column_widths=40, use_table_select=[-1,0,-1,0]
  545.     endelse
  546.   endif
  547. end            ; at_resize_table
  548.  
  549. ; -----------------------------------------------------------------------------
  550. ;
  551. ;  Purpose:  Organize and display a sample record of n lines to demonstrate
  552. ;         how the current defined template interprets the ASCII file.
  553. ;
  554. pro at_sample_record, data
  555.   num_fields = *data.p_num_fields
  556.   lines = (*data.p_rlines)(0:n_elements(num_fields)-1)
  557.   ;
  558.   new_twm = [max(num_fields), n_elements(num_fields)]
  559.  
  560.   at_resize_table, data.tw(6), data.twm, new_twm, /text_table, /last
  561.  
  562.   data.twm = new_twm
  563.   str = strarr(new_twm(0), new_twm(1))
  564.   fpos = 0
  565.  
  566.   ;  Loop for each field.
  567.   ;
  568.   for i=0, n_elements(num_fields)-1 do begin
  569.     for j=0, num_fields(i)-1 do begin
  570.  
  571.       if (j eq num_fields(i)-1) then $        ; last field
  572.         len = strlen(lines(i)) - (*data.p_fields)(fpos).loc $
  573.       else $                                  ; not last field
  574.         len = (*data.p_fields)(fpos+1).loc - (*data.p_fields)(fpos).loc - 1
  575.  
  576.       str(j,i) = strtrim(strmid(lines(i), (*data.p_fields)(fpos).loc, len),2)
  577.       fpos = fpos + 1
  578.     endfor
  579.   endfor
  580.  
  581.   widget_control, data.tw(6), set_value=str
  582.  
  583. end            ; at_sample_record
  584.  
  585. ; -----------------------------------------------------------------------------
  586. ;
  587. ;  Purpose:  Organize and display the field specifications into a table widget.
  588. ;
  589. pro at_set_list, data, just_highlight=just_highlight
  590.  
  591.   groups = *data.p_groups
  592.   if (keyword_set(just_highlight) eq 0) then begin
  593.  
  594.     dstr = ['Skip', 'Byte', 'Integer', 'Long', 'Floating', 'Double', $
  595.             'Complex', 'String']
  596.  
  597.     new_tws = [ ([3,2])(data.mode), long(total(*data.p_num_fields)) ]
  598.  
  599.     at_resize_table, data.tw(8), data.tws, new_tws
  600.     data.tws = new_tws
  601.  
  602.     ;  String array, set to [3,1] in at_widget.
  603.     ;  [0,0] =
  604.     ;
  605.     str = strarr(data.tws(0), data.tws(1))
  606.     gptr = 0
  607.  
  608.     ;  Loop for each group.
  609.     ;
  610.     for i=0, n_elements(groups)-1 do begin
  611.       if (i eq 0) then new_group = 1 $
  612.       else             new_group = (groups(i) ne groups(i-1))
  613.       ptr = where(groups eq groups(i), count)
  614.  
  615.       if (new_group) then begin
  616.         gptr = i
  617.  
  618.         if (count eq 1) then str(0,i) = (*data.p_fields)(i).name $
  619.         else                 str(0,i) = '{1} ' + (*data.p_fields)(i).name
  620.  
  621.         str(1,i) = dstr((*data.p_fields)(i).type)
  622.  
  623.         if (data.mode eq 0) then $
  624.           str(2,i) = strtrim(string((*data.p_fields)(i).loc),2)
  625.  
  626.       endif else begin
  627.  
  628.         str(0,i) = '{' + strtrim(string(i-gptr+1),2) + '}'
  629.  
  630.         if (data.mode eq 0) then $
  631.           str(2,i) = strtrim(string((*data.p_fields)(i).loc),2)
  632.  
  633.       endelse
  634.  
  635.     endfor
  636.  
  637.     if (n_elements(*data.p_num_fields) eq 1) then $
  638.       c_str = reform(str(0,*)) $
  639.     else $
  640.       c_str = strarr(data.twm(0)) + ' '
  641.     widget_control, data.tw(6), column_label=c_str
  642.  
  643.     rstr = strtrim(string(indgen(data.tws(1))+1),2)
  644.     widget_control, data.tw(8), set_value=str, row_labels=rstr, alignment=0
  645.   endif
  646.  
  647.   widget_control, data.tw(8), set_table_select= $
  648.     [0, data.lptr, data.tws(0)-1, data.lptr]
  649.  
  650.   lpos = at_which_field(*data.p_num_fields, data.lptr, col)
  651.   widget_control, data.tw(6), set_table_select=[col,lpos,col,lpos]
  652.  
  653.   ptr = where(groups eq groups(data.lptr), count)
  654.   widget_control, data.dl, sensitive=(ptr(0) eq data.lptr)
  655.   widget_control, data.buts(14), sensitive=(ptr(0) eq data.lptr and count gt 1)
  656. end            ; at_set_list
  657.  
  658. ; -----------------------------------------------------------------------------
  659. ;
  660. ;  Purpose:
  661. ;
  662. pro at_update, data, new_lptr, change=change, new=new
  663.  
  664.  
  665.   if (n_elements(change) eq 0) then change = 0
  666.   if (keyword_set(new) eq 0) then begin
  667.     widget_control, data.tw(4), get_value=name
  668.     widget_control, data.tw(5), get_value=col
  669.     change = change or (name(0) ne (*data.p_fields)(data.lptr).name or $
  670.                         long(col(0)) ne (*data.p_fields)(data.lptr).loc)
  671.     (*data.p_fields)(data.lptr).name = name(0)
  672.     (*data.p_fields)(data.lptr).loc = long(col(0))
  673.   endif
  674.   data.lptr = new_lptr
  675.   at_set_list, data, just_highlight=(change eq 0)
  676.   widget_control, data.tw(4), set_value=(*data.p_fields)(new_lptr).name
  677.   widget_control, data.dl, set_droplist_select= $
  678.     (*data.p_fields)(new_lptr).type
  679.   widget_control, data.tw(5), set_value= $
  680.     strtrim(string((*data.p_fields)(new_lptr).loc),2)
  681.  
  682. end            ; at_update
  683.  
  684. ; -----------------------------------------------------------------------------
  685. ;
  686. ;  Purpose:  Handle events for third page of GUI.
  687. ;
  688. pro at_3_event, ev
  689.  
  690.   widget_control, ev.id, get_uvalue=uvalue
  691.   widget_control, ev.top, get_uvalue=data, /no_copy
  692.  
  693.   if (uvalue eq 'group') then begin
  694.     sel = widget_info(data.tw(8), /table_select)
  695.     if (sel(1) ne sel(3)) then begin
  696.       types = (*data.p_fields)(sel(1):sel(3)).type
  697.       is_string = (total(types eq 7) gt 0)
  698.       if (is_string) then $
  699.         (*data.p_fields)(sel(1):sel(3)).type = (7 * (types ne 0)) $
  700.       else $
  701.         (*data.p_fields)(sel(1):sel(3)).type = (max(types) * (types ne 0))
  702.       (*data.p_groups)(sel(1):sel(3)) = (*data.p_groups)(sel(1))
  703.       at_update, data, data.lptr, /change, /new
  704.     endif
  705.   endif
  706.  
  707.   if (uvalue eq 'ungroup') then begin
  708.     ptr = where(*data.p_groups eq (*data.p_groups)(data.lptr))
  709.     (*data.p_groups)(ptr) = (indgen(n_elements(*data.p_fields)))(ptr)
  710.     (*data.p_fields)(ptr).type = (*data.p_types)(ptr)
  711.     at_set_list, data
  712.   endif
  713.  
  714.   if (uvalue eq 'ungroup all') then begin
  715.     *data.p_groups = indgen(n_elements(*data.p_fields))
  716.     (*data.p_fields).type = *data.p_types
  717.     at_set_list, data
  718.   endif
  719.  
  720.   if (uvalue eq 'ltable') then begin
  721.     ; if selection event...
  722.     if (ev.type eq 4) then $
  723.       if (ev.sel_top eq ev.sel_bottom and ev.sel_top ne -1) then $
  724.         at_update, data, ev.sel_top
  725.   endif
  726.   if (uvalue eq 'table') then begin
  727.     ; if selection event...
  728.     if (ev.type eq 4) then $
  729.       if (ev.sel_top eq ev.sel_bottom and ev.sel_top ne -1) then begin
  730.         tot_num_fields = long(total(*data.p_num_fields))
  731.         if (ev.sel_top eq 0) then add = 0 $
  732.         else add = long(total((*data.p_num_fields)(0:ev.sel_top-1)))
  733.         new_ptr = (ev.sel_left + add) < (tot_num_fields-1)
  734.         at_update, data, new_ptr, /change
  735.       endif
  736.   endif
  737.  
  738.   if (uvalue eq 'list') then begin
  739.     if (ev.index lt n_elements(*data.p_fields)) then $
  740.       at_update, data, ev.index
  741.   endif
  742.  
  743.   if (uvalue eq 'name') then begin
  744.     if (ev.type eq 0) then begin
  745.       bad = (ev.ch lt 48 or (ev.ch gt 57 and ev.ch lt 65) or $
  746.             (ev.ch gt 90 and ev.ch lt 97 and ev.ch ne 95) or ev.ch gt 122l)
  747.       if (bad) then begin
  748.         widget_control, ev.id, set_value=(*data.p_fields)(data.lptr).name
  749.         widget_control, ev.id, set_text_select= $
  750.           strlen((*data.p_fields)(data.lptr).name)
  751.       endif
  752.     endif
  753.     if (keyword_set(bad) eq 0) then begin
  754.       widget_control, ev.id, get_value=name
  755.       if (name(0) ne (*data.p_fields)(data.lptr).name and $
  756.           name(0) ne '') then begin
  757.         (*data.p_fields)(data.lptr).name = name(0)
  758.         at_set_list, data
  759.       endif
  760.     endif
  761.   endif
  762.  
  763.   if (uvalue eq 'type') then begin
  764.     ptr = where(*data.p_groups eq (*data.p_groups)(data.lptr), count)
  765.     if (count eq 1) then begin
  766.       (*data.p_fields)(data.lptr).type = ev.index
  767.       (*data.p_types)(data.lptr) = ev.index
  768.     endif else $
  769.       (*data.p_fields)(ptr).type = (ev.index * ((*data.p_types)(ptr) ne 0))
  770.     at_set_list, data
  771.   endif
  772.  
  773.   if (uvalue eq 'location') then begin
  774.     widget_control, ev.id, get_value=str
  775.     (*data.p_fields)(data.lptr).loc = at_str_to_val(str)
  776.     at_set_list, data
  777.  
  778.     ; Organize and display a sample record of n lines to demonstrate
  779.     ; how the current defined template interprets the ASCII file.
  780.     ;
  781.     at_sample_record, data
  782.  
  783.   endif
  784.  
  785.   if (strmid(uvalue,0,4) eq 'miss') then begin
  786.     data.miss_type = fix(strmid(uvalue,4,1))
  787.     widget_control, data.mb(4), sensitive=data.miss_type
  788.     if (data.miss_type eq 0) then $
  789.       (*data.p_template).missing_value = !values.f_nan $
  790.     else begin
  791.       widget_control, data.tw(0), get_value=str
  792.       (*data.p_template).missing_value = at_str_to_val(str, /floating)
  793.     endelse
  794.   endif
  795.  
  796.   if (uvalue eq 'value') then begin
  797.     widget_control, ev.id, get_value=str
  798.     (*data.p_template).missing_value = at_str_to_val(str, /floating)
  799.   endif
  800.  
  801.   widget_control, ev.top, set_uvalue=data, /no_copy
  802.  
  803. end            ; at_3_event
  804.  
  805. ; -----------------------------------------------------------------------------
  806. ;
  807. ;  Purpose:  Handle events for second page of GUI.
  808. ;
  809. pro at_2_event, ev
  810.   ;
  811.   widget_control, ev.id, get_uvalue=uvalue
  812.   widget_control, ev.top, get_uvalue=data, /no_copy
  813.   if (uvalue eq 'user' or strmid(uvalue,0,7) eq 'delimit') then begin
  814.     if (uvalue eq 'user') then type = 5 $
  815.     else                       type = fix(strmid(uvalue,7,1))
  816.     data.delimit = ([9,59,32,44,58,32])(type)
  817.     widget_control, data.mb(5), sensitive=(type eq 5)
  818.     if (type eq 5) then begin
  819.       widget_control, data.tw(1), get_value=str
  820.       data.delimit = (byte(strmid(str(0),0,1)))(0)
  821.     endif
  822.     ;
  823.     num_fields = at_num_fields(*data.p_rlines, data.delimit, data.comment)
  824.     widget_control, data.tw(2), set_value=at_list_to_str(num_fields)
  825.     *data.p_num_fields = temporary(num_fields)
  826.     data.change = 1
  827.   endif
  828.   if (uvalue eq 'fields') then begin
  829.     widget_control, ev.id, get_value=str
  830.     if (strtrim(strcompress(str(0)),2) eq '') then str = '1'
  831.     *data.p_num_fields = at_str_to_list(str)
  832.     data.change = 1
  833.   endif
  834.   widget_control, ev.top, set_uvalue=data, /no_copy
  835. end            ; at_2_event
  836.  
  837. ; -----------------------------------------------------------------------------
  838. ;
  839. ;  Purpose:  Handle events for first page of GUI.
  840. ;
  841. pro at_1_event, ev
  842.  
  843.   widget_control, ev.id, get_uvalue=uvalue
  844.   widget_control, ev.top, get_uvalue=data, /no_copy
  845.  
  846.   if (uvalue eq 'text') then begin
  847.     widget_control, ev.id, get_value=str
  848.     ds = (at_str_to_val(str)-1)
  849.     if (ds ne data.data_start) then begin
  850.       data.data_start = ds
  851.       widget_control, data.tw(6), set_table_select= $
  852.         [0,data.data_start<(data.twm(1)-1),0,data.data_start<(data.twm(1)-1)]
  853.       data.change = 1
  854.     endif
  855.   endif
  856.  
  857.   if (uvalue eq 'comment') then begin
  858.     widget_control, ev.id, get_value=str
  859.     data.comment = str(0)
  860.     data.change = 1
  861.   endif
  862.  
  863.   if (strmid(uvalue,0,4) eq 'mode') then begin
  864.     data.mode = fix(strmid(uvalue,4,1))
  865.     data.change = 1
  866.   endif
  867.  
  868.   ;  Handle table events.
  869.   ;
  870.   if (uvalue eq 'table') then begin
  871.     ; if selection event...
  872.     if (ev.type eq 4) then $
  873.       if (ev.sel_left eq 0) then begin
  874.         widget_control, data.tw(7), set_value= $
  875.         strtrim(string(ev.sel_top+1),2)
  876.         data.change = 1
  877.       endif
  878.   ;
  879.   ;  Handle "Next n Lines" button event.
  880.   ;
  881.   endif else if (uvalue eq 'next set') then begin
  882.     widget_control, /hourglass
  883.     last_pos = data.last_pos
  884.     new_lines = at_get_lines(data.name, data.browseLines, $
  885.       last_pos=last_pos, end_reached=end_reached)
  886.     data.last_pos = last_pos
  887.     data.end_reached = end_reached
  888.     widget_control, data.mb(7), sensitive=(end_reached eq 0)
  889.     if (n_elements(new_lines) gt 1 or new_lines(0) ne '') then begin
  890.       *data.p_lines = [*data.p_lines, new_lines]
  891.       widget_control, data.tw(6), insert_rows=n_elements(new_lines)
  892.       at_display_text, data, /first
  893.     endif
  894.   endif
  895.  
  896.   widget_control, ev.top, set_uvalue=data, /no_copy
  897.  
  898. end            ; at_1_event
  899.  
  900. ; -----------------------------------------------------------------------------
  901. ;
  902. ;  Purpose:  Control the setting of the three progessive states of the
  903. ;         template definition - sets all of the widgets and pointers
  904. ;         involved and allows movement both forward and backward in
  905. ;         the definition process.
  906. ;
  907. pro at_set_state, data, forward=forward, back=back
  908.   ;
  909.   case data.step of
  910.  
  911.     ; ----------------------------------------
  912.     0: begin
  913.     ; ----------------------------------------
  914.  
  915.       title = 'STEP 1 of 3:  Define Data Type / Range'
  916.  
  917.       widget_control, data.base, tlb_set_title=title
  918.       widget_control, data.buts(9), sensitive=0
  919.       widget_control, data.buts(0), set_button=(data.mode eq 0)
  920.       widget_control, data.buts(1), set_button=data.mode
  921.       widget_control, data.buts(13), sensitive=0
  922.       ;
  923.       widget_control, data.buts(2), set_button=(data.miss_type eq 0)
  924.       widget_control, data.buts(3), set_button=data.miss_type
  925.  
  926.       widget_control, data.mb(4), sensitive=data.miss_type
  927.  
  928.       ;
  929.       widget_control, data.slab, set_value='Selected Text File'
  930.       widget_control, data.mb(7), sensitive=(data.end_reached eq 0), map=1
  931.  
  932.       widget_control, data.tw(7), set_value= $
  933.         strtrim(string(data.data_start+1),2)
  934.       widget_control, data.tw(6), event_pro='at_1_event'
  935.       at_display_text, data, /first
  936.       data.change = 0
  937.     end
  938.  
  939.     ; ----------------------------------------
  940.     1: begin
  941.     ; ----------------------------------------
  942.       ; retreive the data_start value
  943.       widget_control, data.tw(7), get_value=str
  944.       data.data_start =(at_str_to_val(str)-1) > 0
  945.       ;
  946.       ; scan through lines and remove comments and blank lines for
  947.       ; steps two and three (after the start of the data).
  948.       ;
  949.       if (keyword_set(forward)) then begin
  950.         lines = *data.p_lines
  951.         rlines = strarr(n_elements(lines))
  952.         count = 0
  953.         for i=data.data_start, n_elements(lines)-1 do begin
  954.           line = lines(i)
  955.           if (data.comment ne '') then begin
  956.             pos = strpos(line, data.comment)
  957.             if (pos(0) ne -1) then line = strmid(line, 0, pos(0)-1)
  958.           endif
  959.           if (strtrim(line,2) ne '') then begin
  960.             rlines(count) = line
  961.             count = count + 1
  962.           endif
  963.         endfor
  964.         if (count gt 0) then  *data.p_rlines = rlines(0:count-1) $
  965.         else                  *data.p_rlines = ''
  966.       endif
  967.       ;
  968.       widget_control, data.mb(3), map=data.mode
  969.       widget_control, data.buts(9), sensitive=1
  970.       widget_control, data.buts(10), sensitive=1
  971.       widget_control, data.buts(13), sensitive=0
  972.       widget_control, data.mb(7), map=0
  973.  
  974.       widget_control, data.slab, set_value='Selected Text Records'
  975.       if (data.mode eq 1) then begin
  976.         if (keyword_set(forward)) then $
  977.           data.delimit = at_default_delimit((*data.p_rlines)(0), data.comment)
  978.  
  979.         title = 'STEP 2 of 3:  Define Delimiter / Fields'
  980.  
  981.         widget_control, data.base, tlb_set_title=title
  982.         user_defined = (total([59b,58b,32b,44b] eq data.delimit) eq 0)
  983.         if (user_defined) then tstr = string(data.delimit) $
  984.         else                   tstr = ''
  985.         widget_control, data.tw(1), set_value=tstr
  986.         widget_control, data.mb(5), sensitive=user_defined
  987.         widget_control, data.buts(5), set_button=(data.delimit eq 59b)
  988.         widget_control, data.buts(6), set_button=(data.delimit eq 32b)
  989.         widget_control, data.buts(7), set_button=(data.delimit eq 44b)
  990.         widget_control, data.buts(11),set_button=(data.delimit eq 9b)
  991.         widget_control, data.buts(16), set_button=(data.delimit eq 58b)
  992.         widget_control, data.buts(8), set_button=user_defined
  993.       endif else begin
  994.  
  995.         title = 'STEP 2 of 3:  Define Fields'
  996.  
  997.         widget_control, data.base, tlb_set_title=title
  998.       endelse
  999.       ;
  1000.       if ((keyword_set(forward) and data.change) or $
  1001.           n_elements(*data.p_num_fields) eq 0) then $
  1002.         num_fields = at_num_fields(*data.p_rlines, data.delimit,data.comment) $
  1003.       else begin
  1004.         num_fields = *data.p_num_fields
  1005.         new_twm = [2, n_elements(*data.p_lines)]
  1006.         at_resize_table, data.tw(6), data.twm, new_twm, /text_table
  1007.         data.twm = new_twm
  1008.       endelse
  1009.       ;
  1010.       widget_control, data.tw(2), set_value=at_list_to_str(num_fields)
  1011.       *data.p_num_fields = temporary(num_fields)
  1012.       ;
  1013.       widget_control, data.tw(6), event_pro='at_2_event'
  1014.       at_display_text, data
  1015.       data.change = 0
  1016.     end
  1017.  
  1018.     ; ----------------------------------------
  1019.     2: begin
  1020.     ; ----------------------------------------
  1021.  
  1022.       title = 'STEP 3 of 3:  Field Specification'
  1023.  
  1024.       widget_control, data.base, tlb_set_title=title
  1025.       widget_control, data.buts(10), sensitive=0
  1026.       widget_control, data.mb(6), map=(data.mode eq 0)
  1027.       widget_control, data.buts(13), sensitive=1
  1028.       widget_control, data.slab, set_value='Sample Record'
  1029.       ;
  1030.       if (data.miss_type eq 0) then $
  1031.         miss_value = !values.f_nan $
  1032.       else begin
  1033.         widget_control, data.tw(0), get_value=str
  1034.         miss_value = at_str_to_val(str)
  1035.       endelse
  1036.       ;
  1037.       if (n_elements(*data.p_template) eq 0 or data.change) then begin
  1038.  
  1039.     ;  build new template
  1040.     ;
  1041.         template = at_build_template(data, miss_value)
  1042.  
  1043.     ;  delete previous template
  1044.     ;
  1045.         at_delete_template, *data.p_template
  1046.  
  1047.     ;  save new template
  1048.     ;
  1049.         *data.p_template = temporary(template)
  1050.  
  1051.         ; assign a copy of pointers from the template to the data structure
  1052.         ; for simpler access.
  1053.         ;
  1054.         data.p_fields = (*data.p_template).p_fields
  1055.         data.p_groups = (*data.p_template).p_groups
  1056.         *data.p_types = (*data.p_fields).type
  1057.       endif
  1058.  
  1059.       ; Organize and display a sample record of n lines to demonstrate
  1060.       ; how the current defined template interprets the ASCII file.
  1061.       ;
  1062.       at_sample_record, data
  1063.  
  1064.       widget_control, data.tw(6), event_pro='at_3_event'
  1065.       data.lptr = 0
  1066.       at_update, data, data.lptr, /change, /new
  1067.     end
  1068.  
  1069.   endcase
  1070.  
  1071. end            ; at_set_state
  1072.  
  1073. ; -----------------------------------------------------------------------------
  1074. ;
  1075. ;  Purpose:
  1076. ;
  1077. pro at_widget_cleanup, base
  1078.   ;
  1079.   widget_control, base, get_uvalue=data, /no_copy
  1080.   widget_control, base, /destroy
  1081.   if (n_elements(data) eq 0) then return
  1082.   ;
  1083.   ptr_free, data.p_lines
  1084.   ptr_free, data.p_rlines
  1085.   ptr_free, data.p_num_fields
  1086.   ptr_free, data.p_template
  1087.   ptr_free, data.p_types
  1088. end            ; at_widget_cleanup
  1089.  
  1090. ; -----------------------------------------------------------------------------
  1091. ;
  1092. ;  Purpose:
  1093. ;
  1094. pro at_widget_event, ev
  1095.   ;
  1096.   if (tag_names(ev,/struct) eq 'WIDGET_KILL_REQUEST') then begin
  1097.     at_widget_cleanup, ev.top
  1098.     return
  1099.   endif
  1100.   ;
  1101.   widget_control, ev.id, get_uvalue=uvalue
  1102.   widget_control, ev.top, get_uvalue=data, /no_copy
  1103.  
  1104.   if (uvalue eq 'next') then begin
  1105.     widget_control, data.mb(data.step), map=0
  1106.     data.step = data.step + 1
  1107.     widget_control, data.mb(data.step), map=1
  1108.     at_set_state, data, /forward
  1109.   endif
  1110.  
  1111.   if (uvalue eq 'back') then begin
  1112.     widget_control, data.mb(data.step), map=0
  1113.     data.step = data.step - 1
  1114.     widget_control, data.mb(data.step), map=1
  1115.     at_set_state, data, /back
  1116.   endif
  1117.  
  1118.   if (uvalue eq 'finish') then begin
  1119.     *data.p_result = {accept:1, template:*data.p_template}
  1120.     widget_control, ev.top, set_uvalue=data, /no_copy
  1121.     at_widget_cleanup, ev.top
  1122.     return
  1123.   endif
  1124.  
  1125.   if (uvalue eq 'cancel') then begin
  1126.     at_delete_template, *data.p_template
  1127.     widget_control, ev.top, set_uvalue=data, /no_copy
  1128.     at_widget_cleanup, ev.top
  1129.     return
  1130.   endif
  1131.  
  1132.   widget_control, ev.top, set_uvalue=data, /no_copy
  1133.  
  1134. end            ; at_widget_event
  1135.  
  1136. ; -----------------------------------------------------------------------------
  1137. ;
  1138. ;  Purpose:
  1139. ;
  1140. function at_widget, $
  1141.     name, $                    ; IN:
  1142.     GROUP=group, $                ; IN: (opt)
  1143.     CANCEL=cancel, $           ; OUT:
  1144.     BROWSE_LINES=browseLines   ; IN:
  1145.  
  1146.   ;  Set number of lines for browse button.
  1147.   ;
  1148.   if (N_ELEMENTS(browseLines) ne 0) then browseLinesUse = browseLines $
  1149.   else                                   browseLinesUse = 50
  1150.  
  1151.   ; check out the size of the screen and adjust the widget acordingly
  1152.   device, get_screen_size=screen_size
  1153.   table_scr_ysize = ([110,210])(screen_size(1) gt 600)
  1154.   xoff = ((screen_size(0) - 600) / 2) > 0
  1155.   yoff = ((screen_size(1) - 600) / 2) > 0
  1156.  
  1157.   dt_str = ['Skip Field', 'Byte', 'Integer', 'Long Integer', $
  1158.     'Floating Point', 'Double Precision', 'Complex', 'String']
  1159.  
  1160.   lines = at_get_lines(name, browseLinesUse, last_pos=last_pos, $
  1161.     end_reached=end_reached)
  1162.  
  1163.   buts = lonarr(17)
  1164.   tw = lonarr(10)
  1165.   mb = lonarr(8)
  1166.   title = 'STEP 1 of 3:  Define Data Type / Range'
  1167.  
  1168.   ; Create a group if one wasn't specified.
  1169.   if (N_ELEMENTS(group) eq 0) then begin
  1170.       group = widget_base()
  1171.       groupCreated = 1B
  1172.   endif else $
  1173.       groupCreated = 0B
  1174.   base = widget_base(title=title, /column, xoff=xoff, yoff=yoff, $
  1175.     /modal, group=group)
  1176.   sb   = widget_base(base, /column)
  1177.   mbs  = widget_base(sb)
  1178.   ;
  1179.   ; STEP 1 of 3: Define Data Type / Range
  1180.   ;
  1181.   mb(0)= widget_base(mbs, /column, event_pro='at_1_event')
  1182.   sb   = widget_base(mb(0), /column, /frame)
  1183.   lab  = widget_label(sb, $
  1184.     value='Choose the field type which best describes your data:')
  1185.   lab  = widget_label(sb, value='')
  1186.   sb1  = widget_base(sb, /column, /exclusive)
  1187.   buts(0)= widget_button(sb1, /no_release, uvalue='mode0', $
  1188.     value=' Fixed Width  (fields are aligned in columns)')
  1189.   buts(1)= widget_button(sb1, /no_release, uvalue='mode1', $
  1190.     value=' Delimited  (commas, whitespace, etc. separate each field)')
  1191.   ;
  1192.   sb   = widget_base(mb(0), /row, /frame)
  1193.   lab  = widget_label(sb, value=' Comment String to Ignore:  ')
  1194.   tw(9)= widget_text(sb, xs=10, ys=1, /edit, frame=0, /all_events, $
  1195.     uvalue='comment')
  1196.   ;
  1197.   sb   = widget_base(mb(0), /row, /frame)
  1198.   lab  = widget_label(sb, value=' Data Starts at Line:  ')
  1199.   tw(7)= widget_text(sb, xs=5, ys=1, /edit, frame=0, /all_events, $
  1200.     uvalue='text')
  1201.   ;
  1202.   ; STEP 2 of 3: Define Delimiter / Fields
  1203.   ;
  1204.   mb(1)= widget_base(mbs, /column, map=0, event_pro='at_2_event')
  1205.   sb   = widget_base(mb(1), /column, /frame)
  1206.   sb1  = widget_base(sb, /row)
  1207.   lab = widget_label(sb1, value='Number of Fields Per Line:  ')
  1208.   tw(2)= widget_text(sb1, xs=10, ys=1, frame=0, /edit, /all_events, $
  1209.     uvalue='fields')
  1210.   ;
  1211.   mb(3)= widget_base(mb(1), /column, /frame)
  1212.   lab  = widget_label(mb(3), value='Delimiter Between Data Elements:')
  1213.   sb1  = widget_base(mb(3), /row)
  1214.   sb2  = widget_base(sb1, row=2, /exclusive)
  1215.   mb(5)= widget_base(sb1, /row, /align_bottom)
  1216.   tw(1)= widget_text(mb(5), xs=2, ys=1, /edit, frame=0, $
  1217.     /all_events, uvalue='user')
  1218.   buts(6) = widget_button(sb2, value='White Space', uvalue='delimit2', /no_rel)
  1219.   buts(7) = widget_button(sb2, value='Comma',       uvalue='delimit3', /no_rel)
  1220.   buts(16)= widget_button(sb2, value='Colon',       uvalue='delimit4', /no_rel)
  1221.   buts(5) = widget_button(sb2, value='Semicolon',   uvalue='delimit1', /no_rel)
  1222.   buts(11)= widget_button(sb2, value='Tab',         uvalue='delimit0', /no_rel)
  1223.   buts(8) = widget_button(sb2, value='Other:',      uvalue='delimit5', /no_rel)
  1224.   ;
  1225.   ; STEP 3 of 3: Field Specifications
  1226.   ;
  1227.   mb(2)= widget_base(mbs, /column, map=0, event_pro='at_3_event')
  1228.   sb   = widget_base(mb(2), column=2)
  1229.   sb1  = widget_base(sb, /column, /frame)
  1230.   tw(8)= widget_table(sb1, value=strarr(3,1), alignment=0, scr_xsize=225, $
  1231.     scr_ysize=150, uvalue='ltable', column_widths=[100,75,50], $
  1232.     column_labels=['Name','Data Type','Loc'], /ALL_EVENTS, /SCROLL, $
  1233.     /RESIZEABLE_COLUMNS)
  1234.   widget_control, tw(8), column_widths=25, use_table_select=[-1,0,-1,0]
  1235.   ;
  1236.   sb1  = widget_base(sb, /column, /frame)
  1237.   sb2  = widget_base(sb1, /row)
  1238.   lab  = widget_label(sb2, value='Name:  ')
  1239.   tw(4)= widget_text(sb2, xs=18, ys=1, frame=0, /edit, /all_events, $
  1240.     uvalue='name')
  1241.   sb2  = widget_base(sb1, /row)
  1242.   dl   = widget_droplist(sb2, Title='Type:  ', value=dt_str, uvalue='type')
  1243.   mb(6)= widget_base(sb1, /row)
  1244.   lab  = widget_label(mb(6), value='Column')
  1245.   tw(5)= widget_text(mb(6), xs=3, ys=1, frame=0, /edit, /all_events, $
  1246.     uvalue='location')
  1247.   sb2  = widget_base(sb1, /row)
  1248.   but  = widget_button(sb2, value=' Group ', uvalue='group')
  1249.   buts(14) = widget_button(sb2, value=' UnGroup ', uvalue='ungroup')
  1250.   but  = widget_button(sb2, value=' Ungroup All ', uvalue='ungroup all')
  1251.   ;
  1252.   sb   = widget_base(mb(2), /row, /frame)
  1253.   lab  = widget_label(sb, value=' Assign Missing Data:  ')
  1254.   sb1  = widget_base(sb, /row)
  1255.   sb2  = widget_base(sb1, /row, /exclusive)
  1256.   buts(2)= widget_button(sb2, value='IEEE NaN', /no_rel, uvalue='miss0')
  1257.   buts(3)= widget_button(sb2, value='Value:', /no_rel, uvalue='miss1')
  1258.   mb(4)  = widget_base(sb1, /row)
  1259.   tw(0)= widget_text(mb(4), xs=7, ys=1, frame=0, /edit, /all_events, $
  1260.     uvalue='value')
  1261.   ;
  1262.   ; general text widget and <back, next> buttons
  1263.   ;
  1264.   sb   = widget_base(base, /column)
  1265.   sb1  = widget_base(sb, /row)
  1266.   slab = widget_label(sb1, value='Selected Text File       ')
  1267.  
  1268.   mb(7)= widget_base(sb1, /row)
  1269.   widget_control, mb(7), sensitive=(end_reached eq 0)
  1270.  
  1271.   value = '  Read in Next ' + STRTRIM(STRING(browseLinesUse),2) + ' Lines  '
  1272.   but  = widget_button(mb(7), value=value, uvalue='next set', $
  1273.     event_pro='at_1_event')
  1274.   str  = strarr(1,n_elements(lines))
  1275.   tw(6)= widget_table(sb, value=str, alignment=0, scr_xsize=500, $
  1276.     scr_ysize=table_scr_ysize, uvalue='table', column_labels=['Text'], $
  1277.     column_widths=450, /ALL_EVENTS, /SCROLL, /RESIZEABLE_COLUMNS)
  1278.   widget_control, tw(6), column_widths=40, use_table_select=[-1,0,-1,0]
  1279.   ;
  1280.   sb   = widget_base(base, /row, /align_right)
  1281.   sb1  = widget_base(sb, /row, /frame)
  1282.   but  = widget_button(sb1, value=' Cancel ', uvalue='cancel')
  1283.   buts(9) = widget_button(sb1, value=' < Back ', uvalue='back')
  1284.   buts(10)= widget_button(sb1, value=' Next > ', uvalue='next')
  1285.   buts(13)= widget_button(sb1, value=' Finish ', uvalue='finish')
  1286.   widget_control, base, /realize
  1287.   widget_control, tw(8), /input_focus
  1288.   ;
  1289.   p_result = ptr_new({accept:0})
  1290.   data = {base:base, mb:mb, tw:tw, buts:buts, dl:dl, step:0, name:name, $
  1291.     p_lines:ptr_new(lines), p_rlines:ptr_new(/allocate_heap), $
  1292.     p_num_fields:ptr_new(/allocate_heap), p_template:ptr_new(/allocate_heap), $
  1293.     p_types:ptr_new(/allocate_heap), p_fields:ptr_new(), p_groups:ptr_new(), $
  1294.     p_result:p_result, mode:1, data_start:0L, lptr:0, delimit:32b, $
  1295.     miss_type:0, last_pos:last_pos, change:1, end_reached:end_reached, $
  1296.     comment:'', twm:[1,n_elements(lines)], tws:[3,1], slab:slab, $
  1297.     browseLines:browseLinesUse}
  1298.   at_set_state, data
  1299.   widget_control, base, set_uvalue=data, /no_copy
  1300.   xmanager, 'at_widget', base
  1301.   ;
  1302.   if (groupCreated) then $
  1303.       widget_control, group, /destroy
  1304.  
  1305.   cancel = ((*p_result).accept eq 0)
  1306.   if ((*p_result).accept) then template = (*p_result).template $
  1307.   else                         template = 0
  1308.   ptr_free, p_result
  1309.   return, template
  1310. end            ; at_widget
  1311.  
  1312.  
  1313. ; -----------------------------------------------------------------------------
  1314. ;
  1315. ;  Purpose: Check that the input filename is a string, exists, and appears
  1316. ;           to be ASCII... 
  1317. ;
  1318. function at_check_file, fname
  1319.   catch, error_status
  1320.   if (error_status ne 0) then begin
  1321.     if (n_elements(unit) gt 0) then free_lun, unit
  1322.     return, -3 ; unexpected error reading from file
  1323.   endif
  1324.   ;
  1325.   info = size(fname)
  1326.   if (info(info(0)+1) ne 7) then return, -1 ; filename isn't a string
  1327.   ;
  1328.   openr, unit, fname, error=error, /get_lun
  1329.   if (error eq 0) then begin
  1330.     finfo = fstat(unit)
  1331.     ; set non-ascii values in lookup table
  1332.     lut = bytarr(256) + 1b
  1333.     lut[7:13]   = 0b
  1334.     lut[32:127] = 0b
  1335.     data = bytarr(32767<finfo.size, /nozero)
  1336.     readu, unit, data
  1337.     free_lun, unit
  1338.     carriage_return = (total(data eq 10b) gt 0 or total(data eq 13b) gt 0) 
  1339.     if (carriage_return eq 0) then return, -4 ; looks like a binary file
  1340.     non_printable   = (total(lut(data)) gt 0)
  1341.     if (non_printable) then return, -4 $ ; looks like a binary file
  1342.     else                    return, 0    ; everything is cool
  1343.   endif else $
  1344.     return, -2 ; unable to open file
  1345. end
  1346.  
  1347. ; -----------------------------------------------------------------------------
  1348. ;
  1349. ;  Purpose:  The main routine.
  1350. ;
  1351. function ascii_template, $
  1352.     file, $            ; IN: (opt)
  1353.     BROWSE_LINES=browseLines, $    ; IN: (opt)
  1354.     GROUP=group, $    ; IN: (opt)
  1355.     CANCEL=cancel        ; OUT: (opt)
  1356.  
  1357.   ;  Set to return to caller on error.
  1358.   ;
  1359.   ON_ERROR, 2
  1360. ;  ON_ERROR, 0
  1361.  
  1362.   ;  Set some defaults.
  1363.   ;
  1364.   cancel = 0
  1365.  
  1366.   ;  Set number of lines for browse button.
  1367.   ;
  1368.   if (N_ELEMENTS(browseLines) ne 0) then browseLinesUse = browseLines $
  1369.                                     else browseLinesUse = 50
  1370.  
  1371.   ;  If no file specified, use DIALOG_PICKFILE.
  1372.   ;
  1373.   if (n_elements(file) eq 0) then begin
  1374.     file = DIALOG_PICKFILE(/MUST_EXIST, GROUP=group)
  1375.     if (file eq '') then RETURN, 0
  1376.   endif
  1377.   
  1378.   ; check that the file is readable and appears to be ASCII
  1379.   ;
  1380.   ret = at_check_file(file)
  1381.   case ret of
  1382.     -1: MESSAGE, 'File name must be a string.'
  1383.     -2: MESSAGE, 'File "' + file + '" not found.'
  1384.     -3: MESSAGE, 'Error Reading from file "' + file + '"
  1385.     -4: MESSAGE, 'File "' + file + '" is not an ASCII file.'
  1386.     else:
  1387.   endcase
  1388.  
  1389.   ;  Put up the GUI.
  1390.   ;
  1391.   templateWithPtrs = $
  1392.     at_widget(file, cancel=cancel, BROWSE_LINES=browseLinesUse, GROUP=group)
  1393.  
  1394.   ;  If user canceled, return 0.
  1395.   ;
  1396.   if (cancel) then RETURN, 0
  1397.  
  1398.   ;  Restructure template to eliminate pointers and return it.
  1399.   ;  (Include a version number for easier processing if modify
  1400.   ;  the template definition later.)
  1401.   ;
  1402.   template = { $
  1403.     version:        1.0, $
  1404.     dataStart:        templateWithPtrs.record_start_loc, $
  1405.     delimiter:        templateWithPtrs.delimit, $
  1406.     missingValue:    templateWithPtrs.missing_value, $
  1407.     commentSymbol:    templateWithPtrs.comment_symbol, $
  1408.     fieldCount:        *templateWithPtrs.p_num_fields, $
  1409.     fieldTypes:        (*templateWithPtrs.p_fields).type, $
  1410.     fieldNames:        (*templateWithPtrs.p_fields).name, $
  1411.     fieldLocations:    (*templateWithPtrs.p_fields).loc, $
  1412.     fieldGroups:    *templateWithPtrs.p_groups $
  1413.     }
  1414.   at_delete_template, templateWithPtrs
  1415.   RETURN, template
  1416.  
  1417. end            ; ascii_template
  1418.  
  1419. ; -----------------------------------------------------------------------------
  1420.  
  1421.